package com.summer.safe.config.shiro;

import com.summer.safe.config.filter.KickoutControlFilter;
import com.summer.safe.config.filter.ShiroFormAuthcFilter;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 配置shiro 过滤器的管理
     *
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        bean.setSecurityManager(securityManager);
        //配置登录的url
        bean.setLoginUrl("/login");
        //登录成功的url
        bean.setSuccessUrl("/index");
        //未授权界面;
        bean.setUnauthorizedUrl("/403");

        Map<String, Filter> filters = bean.getFilters();
        //限制同一用户多设备登录
        filters.put("kickout", kickoutControlFilter());
        filters.put("authc", shiroAuthcFilter());
        bean.setFilters(filters);
        //配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //anon表示匿名访问，authc表示需要认证才可以访问
        //静态文件
        filterChainDefinitionMap.put("/static/**", "anon");
        //SQl监控
        filterChainDefinitionMap.put("/druid/**" , "anon");
        //访问文件路径
        filterChainDefinitionMap.put("/uploadfiles/**", "anon");
        //验证码
        filterChainDefinitionMap.put("/kaptcha", "anon");
        //登录需要验证(如果设置为anon则不执行表单验证的方法)

        //index设置需要走的过滤方法为kickout,即验证同一个用户是否多设备登录
        filterChainDefinitionMap.put("/index", "kickout");
        //首先验证是否是已经登录,在进行权限验证/**
        filterChainDefinitionMap.put("/**", "kickout,authc");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }


    /**
     * 配置shiro session的管理
     *
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 加入缓存管理器
        sessionManager.setCacheManager(ehCacheManager());
        // 会话过期时间，单位：毫秒(在无操作时开始计时)
        sessionManager.setGlobalSessionTimeout(1800000);
        // 是否定时检查session
        sessionManager.setSessionValidationSchedulerEnabled(true);
        // tomcat的JESSIONID自动生成模块
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        // 删除过期的session
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionDAO(enterCacheSessionDAO());
        return sessionManager;
    }


    /**
     * EnterpriseCacheSessionDAO shiro sessionDao层的实现；
     * 提供了缓存功能的会话维护，默认情况下使用MapCache实现，内部使用ConcurrentHashMap保存缓存的会话。
     */
    public EnterpriseCacheSessionDAO enterCacheSessionDAO() {
        EnterpriseCacheSessionDAO enterCacheSessionDAO = new EnterpriseCacheSessionDAO();
        //添加缓存管理器
        //添加ehcache活跃缓存名称（必须和ehcache缓存名称一致）
        enterCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        return enterCacheSessionDAO;

    }




    /**
     * SecurityManager 配置管理
     * @return
     */
    @Bean
    public SecurityManager  securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义realm
        securityManager.setRealm(realm());
        // 启用shiro缓存
        securityManager.setCacheManager(ehCacheManager());
        // session管理
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    /**
     * 注入自定义的realm，告诉shiro如何获取用户信息来做登录或权限控制
     */
    @Bean
    public Realm realm() {
        RealmConfig realm = new RealmConfig();
        // shiro缓存设置
        realm.setCacheManager(ehCacheManager());
        //密码比较
        realm.setCredentialsMatcher(customCredentialsMatcher());
        return realm;
    }

    /**
     * 配置自定义的密码比较器
     */

    public CustomCredentialsMatcher customCredentialsMatcher() {
        return new CustomCredentialsMatcher();
    }

    /**
     * 配置shiro表单拦截器
     *
     */
    public ShiroFormAuthcFilter shiroAuthcFilter(){
        return  new ShiroFormAuthcFilter();
    }
    /**
     * 启用缓存,使用自定义缓存
     **/
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return cacheManager;
    }


    /**
     * 限制同一个账户多个设备登录
     * @return
     */
    public KickoutControlFilter kickoutControlFilter(){
        KickoutControlFilter kickoutFilter=new KickoutControlFilter();
        //用于根据会话ID，获取会话进行踢出操作的；
        kickoutFilter.setSessionManager(sessionManager());
        kickoutFilter.setCacheManager(ehCacheManager());
        // 同一个用户最大的会话数，默认-1无限制；比如2的意思是同一个用户允许最多同时两个人登录
        kickoutFilter.setMaxSession(1);
        // 是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序
        kickoutFilter.setKickoutAfter(false);
        //重定向的地址
        kickoutFilter.setKickoutUrl("/login?kickout=1");
        return kickoutFilter;

    }






    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}
